
#include <stdint.h>
#include <string.h>
#include "error_code.h"
#include "bit.h"
#include "base.h"
#include "calculate.h"
#include "etu_timer.h"
#include "character_module.h"
#include "slot.h"

#ifndef NDEBUG
#define NDEBUG
#endif

#include "Log.h"

#define I_BLOCK_MARK	0x80
#define I_BLOCK_FLAG	0x00

#define R_BLOCK_MARK	0xC0
#define R_BLOCK_FLAG	0x80

#define S_BLOCK_MARK	0xC0
#define S_BLOCK_FLAG	0xC0

#define I_SN_BIT_NUM	6
#define I_CHAIN_BIT_NUM	5
#define R_SN_BIT_NUM	4
#define S_REQ_BIT_NUM	5

#define S_REQU			0
#define S_RESP			(1 << S_REQ_BIT_NUM)
#define S_TYPE_MARK		0x1F

#define S_RESYNC		0
#define S_IFS			1
#define S_ABORT			2
#define S_BWTX			3

#define IFSD			0xFE


enum
{
    CORRECT = MAX_ERR_CODE,
    RETRANSMIT,
    TRANSMIT_R,
    RESYNCHRONIZE,
    CHAIN_IN
};

typedef struct
{
    uint8_t NAD;
    uint8_t PCB;
    uint8_t LEN;
    uint8_t *INF;
    uint8_t DEC;
    uint8_t inf_size;
} block_t;

static uint32_t is_I_block(block_t *block);
static uint32_t is_R_block(block_t *block);
static uint32_t is_S_block(block_t *block);
static uint8_t get_I_block_SN(block_t *block);
static uint8_t get_R_block_SN(block_t *block);

static void PrintBlock(const char* tag, block_t* block){
	char blockHead[16];
	if(is_I_block(block)){
		sprintf(blockHead, "I(%02X)", get_I_block_SN(block));
	}
	else
	if(is_R_block(block)){
		sprintf(blockHead, "R(%02X)", get_R_block_SN(block));
	}
	else
	if(is_S_block(block)){
		//sprintf(blockHead, "S", get_I_block_SN(block));
		strcpy(blockHead, "S");
	}
	LOGD(NULL, "**************");
	LOG_SBUF(tag, (uint8_t*)block, 3);
	LOG_SBUF(blockHead, block->INF, block->LEN);
	LOGD(NULL, " %02X", block->DEC);
	LOGD(NULL, "--------------");
}

typedef struct
{
    uint8_t *c_apdu;
    uint32_t c_apdu_len;
    uint32_t out_offset;

    uint8_t *r_apdu;
    uint32_t in_offset;
    uint8_t error_countor;
} transmission_t;


static void init_nak_block(block_t *nak, uint8_t sn, uint32_t err);


static uint8_t get_block_LRC(block_t *block)
{
    uint8_t ck;

    ck = block->NAD;
    ck ^= block->PCB;
    ck ^= block->LEN;

    if(block->LEN > 0)
    {
        ck ^= Buffer_Xor(block->INF, block->LEN);
    }

    return ck;
}

static void init_block_send(block_t *block, uint8_t pcb, uint8_t inf[], uint8_t inf_length)
{
    block->NAD = 0;
    block->PCB = pcb;
    block->LEN = inf_length;
    block->INF = inf;
    block->DEC = get_block_LRC(block);
}

static void send_block(slot_t *slot, block_t *block)
{
    character_module_t *module = slot->module;
    icc_t *icc = slot->icc;
    uint32_t guard_time = icc->extra_guardtime;
	
    ETUTimer_Wait(6, module->FiDi);

    CharacterModule_SendData(module, &block->NAD, 1, guard_time);
    CharacterModule_SendData(module, &block->PCB, 1, guard_time);
    CharacterModule_SendData(module, &block->LEN, 1, guard_time);
    CharacterModule_SendData(module, block->INF, block->LEN, guard_time);
    CharacterModule_SendData(module, &block->DEC, 1, 0);
}

static void init_block_receive(block_t *block, uint8_t inf[], uint8_t inf_size)
{
    memset(block, 0, sizeof(block_t));
    block->INF = inf;
    block->inf_size = inf_size;
}

static uint32_t dummy_receive(character_module_t *module, uint8_t len, uint32_t wait_time)
{
    uint8_t dummy;
    uint8_t i;
    uint32_t ret;
	uint32_t status = NO_ERR;
	
    for(i = 0; i < len; i++)
    {
        ret = CharacterModule_ReceiveData(module, &dummy, 1, wait_time);

        if(ret != NO_ERR)
        {
        	if(!status)
        		status = ret;
        	if(status == ERR_TIMEOUT)
        	{
				TRACE_E("rx dummy timeout(%u)!", wait_time);
            	break;
            }
        }
    }

    return status;
}

static uint32_t receive_block_inf(slot_t *slot, uint8_t inf[], uint8_t inf_len)
{
	uint32_t ret;
	uint32_t status = NO_ERR;
	uint8_t i;
	character_module_t *module = slot->module;
	uint32_t CWT = slot->icc->CWT;

	for(i = 0; i < inf_len; i++)
	{
		ret = CharacterModule_ReceiveData(module, inf + i, 1, CWT);

        if(ret != NO_ERR)
        {
        	LOGE("rxinf", "len=%d,i=%d, err=%d", inf_len, i, ret);
        	if(!status)
            	status = ret;

            if(ret == ERR_TIMEOUT)
            {
				TRACE_E("rx INF timeout(%u)!", CWT);
            	break;
            }
        }
	}
	
	return status;
}

static uint32_t receive_block(slot_t *slot, block_t *block)
{
    uint32_t ret;
    uint32_t status = NO_ERR;
    icc_t *icc = slot->icc;
    character_module_t *module = slot->module;
    uint32_t CWT = icc->CWT;
	uint32_t BWT = icc->MultipleBWT * icc->BWT;
//		ETUTimer_SetCountMultiple(icc->MultipleBWT);
    ret = CharacterModule_ReceiveData(module, &block->NAD, 1, BWT);

    if(ret != NO_ERR)
    {
    	if(!status)
        	status = ret;

        if(ret == ERR_TIMEOUT)
        {
        	TRACE_E("rx NAD timeout(%u)!", BWT);
            goto receive_timeout;
        }
    }

    ret = CharacterModule_ReceiveData(module, &block->PCB, 1, CWT);

    if(ret != NO_ERR)
    {
    	if(!status)
        	status = ret;

        if(ret == ERR_TIMEOUT)
        {
        	TRACE_E("rx PCB timeout(%u)!", CWT);
            goto receive_timeout;
        }
    }

    ret = CharacterModule_ReceiveData(module, &block->LEN, 1, CWT);

    if(ret != NO_ERR)
    {
    	if(!status)
        	status = ret;

        if(ret == ERR_TIMEOUT)
        {
        	TRACE_E("rx LEN timeout(%u)!", CWT);
            goto receive_timeout;
        }
    }

    if(block->LEN > 0)
    {
        uint8_t rx_len = min(block->LEN, block->inf_size);
        
        ret = receive_block_inf(slot, block->INF, rx_len);

        if(ret != NO_ERR)
        {
			if(!status)
				status = ret;

            if(ret == ERR_TIMEOUT)
            {
				TRACE_E("rx INF timeout!");
                goto receive_timeout;
            }
        }

        if(block->LEN > rx_len)
        {
            ret = dummy_receive(module, block->LEN - rx_len, CWT);
			TRACE_E("rx overload!");
            if(ret == ERR_TIMEOUT)
            {
                status = ret;
                goto receive_timeout;
            }
			if(!status){
				status = ERR_BLOCK_FORMAT;
        }
    }
    }

    ret = CharacterModule_ReceiveData(module, &block->DEC, 1, CWT);

    if(ret != NO_ERR)
    {
		if(!status)
			status = ret;

        if(ret == ERR_TIMEOUT)
        {
			TRACE_E("rx DEC timeout(%u)!", CWT);
            goto receive_timeout;
        }
    }

receive_timeout:
    CharacterModule_TimerStop(slot->module);
    return status;
}

static uint32_t check_block_DEC(block_t *block)
{
    uint8_t dec;

    dec = get_block_LRC(block);

    return dec == block->DEC;
}



static uint8_t get_I_block_SN(block_t *block)
{
    return Bit_get(block->PCB, I_SN_BIT_NUM);
}

static uint8_t get_R_block_SN(block_t *block)
{
    return Bit_get(block->PCB, R_SN_BIT_NUM);
}

static uint32_t is_chain_block(block_t *block)
{
    return (block->PCB & (1 << I_CHAIN_BIT_NUM)) != 0;
}

static uint32_t is_I_block(block_t *block)
{
    return (block->PCB & I_BLOCK_MARK) == I_BLOCK_FLAG;
}

static uint32_t is_R_block(block_t *block)
{
    return (block->PCB & R_BLOCK_MARK) == R_BLOCK_FLAG;
}

static uint32_t is_S_block(block_t *block)
{
    return (block->PCB & S_BLOCK_MARK) == S_BLOCK_FLAG;
}

static uint32_t is_valid_PCB_of_I_block(unsigned char pcb)
{
    return ((pcb & I_BLOCK_MARK) == I_BLOCK_FLAG) && ((pcb & 0x1F) == 0);
}

static uint32_t is_valid_PCB_of_R_block(unsigned char pcb)
{
    return ((pcb & R_BLOCK_MARK) == R_BLOCK_FLAG) && ((pcb & 0x23) < 0x03);
}

static uint32_t is_valid_PCB_of_S_block(unsigned char pcb)
{
    if((pcb & S_BLOCK_MARK) != S_BLOCK_FLAG)
    {
        return 0;
    }

    return ((pcb & S_TYPE_MARK) < 0x04) ;
}

static uint32_t is_PCB_valid(block_t *block)
{
    uint8_t pcb = block->PCB;

    return is_valid_PCB_of_I_block(pcb)
           || is_valid_PCB_of_R_block(pcb)
           || is_valid_PCB_of_S_block(pcb);
}

static uint32_t is_LEN_valid(block_t *block)
{
    uint8_t len = block->LEN;


    if(is_I_block(block))
    {
    /*
	This specification does not support I-blocks with LEN = 0. 
    */
        return /*0 < len &&*/ len <= IFSD;
    }

    if(is_R_block(block))
    {
        return len == 0;
    }

    if(is_S_block(block))
    {
        return len < 2;
    }

    return 0;
}

static uint32_t is_NAD_valid(block_t *block)
{
/*
The first block sent by the terminal following the ATR and all follo wing blocks transmitted by either the 
terminal or ICC shall have the NAD = '00'.If during the card session the terminal  or ICC receives a block with a NAD  != '00', 
it may treat the block as invalid.
*/
    return block->NAD == 0;
}

static uint32_t is_block_format_correct(block_t *block)
{
    return is_PCB_valid(block)
           && is_LEN_valid(block)
           && is_NAD_valid(block);
}

static uint32_t block_exchange(slot_t *slot, block_t *block_send, block_t *block_receive)
{
    uint32_t ret;
	
	PrintBlock("tx", block_send);
    send_block(slot, block_send);
    ret = receive_block(slot, block_receive);

    if(ret == NO_ERR)
    {
    	PrintBlock("rx", block_receive);
    	if(!check_block_DEC(block_receive))
    	{
			return ERR_DEC;
    	}
    	
        if(!is_block_format_correct(block_receive))
        {
            ret = ERR_BLOCK_FORMAT;
        }
    }

	if(ret){
		LOGE("exchange", "fail %d", ret);
	}
    return ret;
}

static uint32_t is_S_request_block(block_t *block)
{
    return is_S_block(block) && (Bit_get(block->PCB, S_REQ_BIT_NUM) == 0u);
}

static uint32_t is_S_response_block(block_t *block)
{
    return is_S_block(block) && (Bit_get(block->PCB, S_REQ_BIT_NUM) == 1u);
}

static uint8_t get_S_request_type(block_t *block)
{
    return block->PCB & S_TYPE_MARK;
}

//static uint32_t is_S_request_support(uint8_t request_type)
//{
//    return request_type == S_IFS || request_type == S_BWTX || request_type == S_ABORT;
//}

static uint32_t is_R_SN_EQ_SN_out(block_t *block, icc_t *icc)
{
    return get_R_block_SN(block) == (icc->SN_out & 0x01);
}

static uint32_t is_I_SN_valid(block_t *block, icc_t *icc)
{
/*
	I-blocks are numbered using a modulo-2 number coded on one bit.
*/
    return get_I_block_SN(block) == (icc->SN_in & 0x01);
}

static void set_S_direction(uint8_t *PCB, uint8_t dir)
{
    if(dir == S_REQU)
    {
        Bit_clear(*PCB, S_REQ_BIT_NUM);
    }
    else
    {
    	Bit_set(*PCB, S_REQ_BIT_NUM);        
    }
}

//static void set_S_type(uint8_t *PCB, uint8_t type)
//{
//    *PCB &= (~S_TYPE_MARK);
//    *PCB |= (type & S_TYPE_MARK);
//}

static void init_S_block(block_t *block, uint8_t PCB,
                         uint8_t inf[], uint8_t inf_len)
{
    init_block_send(block, PCB, inf, inf_len);
}

static void set_R_block_SN(uint8_t *PCB, uint8_t SN_in)
{
    if(SN_in & 0x01)
    {
        Bit_set(*PCB, R_SN_BIT_NUM);
    }
    else
    {
        Bit_clear(*PCB, R_SN_BIT_NUM);
    }
}

static void init_R_block(block_t *block, uint8_t SN_in)
{
    uint8_t PCB = R_BLOCK_FLAG;

    set_R_block_SN(&PCB, SN_in);
    init_block_send(block, PCB, (uint8_t *)0, 0);
}


static void set_I_block_SN(uint8_t *PCB, uint8_t SN)
{
    if(SN & 0x01)
    {
        Bit_set(*PCB, I_SN_BIT_NUM);
    }
    else
    {
        Bit_clear(*PCB, I_SN_BIT_NUM);
    }
}

static void set_I_block_chain(uint8_t *PCB, uint8_t is_chain)
{
    if(is_chain)
    {
        Bit_set(*PCB, I_CHAIN_BIT_NUM);
    }
    else
    {
        Bit_clear(*PCB, I_CHAIN_BIT_NUM);
    }
}

static void init_I_block(block_t *block, icc_t *icc, transmission_t *transmission)
{
    uint8_t PCB = I_BLOCK_FLAG;
    uint32_t remain_data_len = transmission->c_apdu_len - transmission->out_offset;
    uint32_t inf_len = min(icc->IFSC, remain_data_len);
    uint8_t *inf = transmission->c_apdu + transmission->out_offset;

    set_I_block_SN(&PCB, icc->SN_out);
    set_I_block_chain(&PCB, remain_data_len > inf_len);
    init_block_send(block, PCB, inf, inf_len);
}
static uint32_t is_response_type_valid(block_t *request_block, block_t *response_block)
{
    return get_S_request_type(request_block) == get_S_request_type(response_block);
}

static uint32_t is_response_inf_valid(block_t *request_block, block_t *response_block)
{
    if(request_block->LEN == response_block->LEN)
    {
        if(request_block->LEN == 0)
        {
            return 1;
        }

        return memcmp(request_block->INF, response_block->INF, request_block->LEN) == 0;
    }

    return 0;
}

static uint32_t is_response_valid(block_t *request_block, block_t *response_block)
{
    return is_S_response_block(response_block)
           && is_response_type_valid(request_block, response_block)
           && is_response_inf_valid(request_block, response_block);
}


static uint32_t request_session(slot_t *slot, uint8_t PCB, uint8_t req_inf[], uint8_t req_inf_len)
{
    uint32_t ret;
    uint32_t error_count;
    block_t s_request_block;
    block_t s_response_block;
    uint8_t resp_inf[2];

    init_S_block(&s_request_block, PCB, req_inf, req_inf_len);
    init_block_receive(&s_response_block, resp_inf, req_inf_len);

    for(error_count = 0; error_count < 3; error_count++)
    {
        ret = block_exchange(slot, &s_request_block, &s_response_block);

        if(ret == NO_ERR)
        {
            if(is_response_valid(&s_request_block, &s_response_block))
            {
                break;
            }
            else
            {
            	
                ret = ERR_RESPONSE;
            }
        }
        else
        {
        	/*
        	if(ret == ERR_DEC)
        	{
				Led_On(RED_LED);
			}
			else
			if(ret == ERR_BLOCK_FORMAT)
			{
				Led_On(BLUE_LED);
			}
			*/
        }
    }

    return ret;
}

uint32_t BlockProtocol_ResynchronizationRequest(slot_t *slot)
{
    uint8_t PCB = S_BLOCK_MARK | S_REQU | S_RESYNC;
    uint32_t ret;

    ret = request_session(slot, PCB, 0, 0);

    if(ret == NO_ERR)
    {
        slot->icc->SN_in = 0;
        slot->icc->SN_out = 0;
    }

    return ret;
}


uint32_t BlockProtocol_IFSDRequest(slot_t *slot)
{
    uint8_t PCB = S_BLOCK_MARK | S_REQU | S_IFS;
    uint8_t IFS = IFSD;
    uint32_t ret;
    uint32_t i;

    for(i = 0; i < 3; i++)
    {
        ret = request_session(slot, PCB, &IFS, sizeof(IFS));

        if(ret == NO_ERR)
        {
            break;
        }

        ret = BlockProtocol_ResynchronizationRequest(slot);

        if(ret != NO_ERR)
        {
            break;
        }
    }

    return ret;
}


static inline uint32_t is_valid_IFSC(uint8_t IFSC)
{
    return (0x10 <= IFSC) && (IFSC <= 0xFE);
}

static uint32_t process_request(slot_t *slot, block_t *block_send, block_t *request_block)
{
    uint32_t ret = NO_ERR;
    uint8_t request_type = get_S_request_type(request_block);
    icc_t *icc = slot->icc;

    switch(request_type)
    {
        case S_IFS:
        {
            uint8_t IFSC = request_block->INF[0];

            if(request_block->LEN == 1 && is_valid_IFSC(IFSC))
            {
                icc->IFSC = IFSC;
            }
            else
            {
                ret = ERR_REQUEST;
            }
        }
        break;

        case S_BWTX:
        {
            if(request_block->LEN == 1)
            {
            	uint8_t mult = request_block->INF[0];
                if(mult > 0)
                {
					icc->MultipleBWT = mult;
                }
				else{
	                icc->MultipleBWT = 1;
				}
            }
            else
            {
                ret = ERR_REQUEST;
            }
        }
        break;

        case S_ABORT:
        {
			ret = ERR_ABORT;
        }
        break;

        default:
        {
            ret = ERR_REQUEST;
        }
    }

    return ret;
}

static void set_response_block(block_t *response, block_t *request)
{
	uint8_t PCB;

	*response = *request;
	PCB = request->PCB;
	set_S_direction(&PCB, S_RESP);
	init_block_send(response, PCB, request->INF, request->LEN);
}

/*Response the S-Request*/
static uint32_t response_S_block(slot_t *slot, block_t *block_send, block_t *block_receive)
{
    uint32_t ret;
    block_t block_response;
    icc_t *icc = slot->icc;

    do
    {
        ret = process_request(slot, block_send, block_receive);

        if(ret != NO_ERR)
        {
            break;
        }

        icc->error_countor = 0;
        set_response_block(&block_response, block_receive);
        ret = block_exchange(slot, &block_response, block_receive);
		icc->MultipleBWT = 1;
//			ETUTimer_SetCountMultiple(icc->MultipleBWT);
        if(ret != NO_ERR)
        {
            break;
        }
    }
    while(is_S_request_block(block_receive));

    return ret;
}

static uint32_t process_response_for_chain_I_block(icc_t *icc,
        block_t *i_block, block_t *response_block)
{
    if(is_R_block(response_block))
    {
    	/*
		Chain_I_Block should be responsed by R-block
    	*/
        if(is_R_SN_EQ_SN_out(response_block, icc))
        {
            /*
			ICC expect previous block
            */
            return RETRANSMIT;
        }
        else
        {
        	/*
			ICC expect next block
        	*/
            return CORRECT;
        }
    }
    else
    {
    	/*
		unexpect block received
    	*/
        return TRANSMIT_R;
    }
}

static uint32_t process_response_for_nonchain_I_block(icc_t *icc,
        block_t *i_block, block_t *response_block)
{
    if(is_I_block(response_block)
            && is_I_SN_valid(response_block, icc))
    {
    	/*
		nonchain_I_block should be responsed by I_block
    	*/
    	if(response_block->LEN > 0) 
    	{
        	return CORRECT;
        }
        else
        {
			return TRANSMIT_R;
        }
    }

    if( is_R_block(response_block))
    {
    /*
		ICC expect previous block
    */
    	if(is_R_SN_EQ_SN_out(response_block, icc))
    	{
    		return RETRANSMIT;
    	}
    	else
    	{
    		/*error notification for first I-block ,ack*/
    		icc->error_countor = 0;
			return TRANSMIT_R;
    	}
    }
    else
    {
        return TRANSMIT_R;
    }

}

static uint32_t process_response_for_I_block(icc_t *icc,
        block_t *i_block, block_t *response_block)
{
    uint32_t ret;

    if(is_chain_block(i_block))
    {
        ret = process_response_for_chain_I_block(icc, i_block, response_block);
    }
    else
    {
        ret = process_response_for_nonchain_I_block(icc, i_block, response_block);
    }

    return ret;
}

static uint32_t process_response_for_R_block(icc_t *icc, block_t *response_block)
{
/*
	The terminal send a R-Block, expect an I-Block.
*/
    if(is_I_block(response_block)
            && is_I_SN_valid(response_block, icc))
    {
        return CORRECT;
    }
    else
    {
    /*
	ICC repsonse is unexpect.
    */
        return TRANSMIT_R;
    }
}

static void error_handle(uint32_t status, uint32_t error, icc_t *icc, block_t *retransmit, block_t *block_send)
{
    switch(status)
    {
        case RETRANSMIT:
        {
            *retransmit = *block_send;
        }
        break;

        case TRANSMIT_R:
        {
            //init_R_block(retransmit, icc->SN_in);
            init_nak_block(retransmit, icc->SN_in, error);
        }
        break;

        case RESYNCHRONIZE:
        {

        }
        break;
    }
}

static uint32_t information_transfer(slot_t *slot, block_t *block_send, block_t *block_receive)
{
    uint32_t ret;
    uint32_t status;
    block_t temp_block = *block_send;
    //uint32_t error_countor = 0;
    icc_t *icc = slot->icc;

    icc->error_countor = 0;

    do
    {
        ret = block_exchange(slot, &temp_block, block_receive);

        if(ret == NO_ERR)
        {
            if(is_S_request_block(block_receive))
            {
                ret = response_S_block(slot, block_send, block_receive);

                if(ret != NO_ERR)
                {                    
					if(ret == ERR_ABORT)
					{
						return ret;
					}
                    icc->error_countor++;
                    if(icc->error_countor < 3)
                    {
                    	init_nak_block(&temp_block, icc->SN_in, ret);
                    	continue;
                    }
                    else
                    {
						break;
                    }
                }
            }

            if(is_R_block(block_send))
            {
                status = process_response_for_R_block(icc, block_receive);
            }
            else if(is_I_block(block_send))
            {
                status = process_response_for_I_block(icc, block_send, block_receive);
            }
            else
            {
                return ERR_INVALID_BLOCK;
            }
        }
        else
        {
            status = TRANSMIT_R;
        }

        if(status == CORRECT)
        {
            return NO_ERR;
        }

        error_handle(status, ret, icc, &temp_block, block_send);
        icc->error_countor++;
    }
    while(icc->error_countor < 3);

    return ERR_OVER_RETRANSMIT;
}

static uint32_t information_transfer_out(slot_t *slot, transmission_t *transmission)
{
    uint32_t ret;
    icc_t *icc = slot->icc;
    block_t block_send, block_receive;

    do
    {
        init_I_block(&block_send, icc, transmission);
        init_block_receive(&block_receive, transmission->r_apdu + transmission->in_offset, IFSD);
        ret = information_transfer(slot, &block_send, &block_receive);

        if(ret != NO_ERR)
        {
            return ret;
        }

        transmission->out_offset += block_send.LEN;
        icc->SN_out++;
        if(is_I_block(&block_receive))
        {
			transmission->in_offset += block_receive.LEN;
			icc->SN_in++;
			if(transmission->in_offset > 1024)
			{
				return ERR_OVER_BUFFER_SIZE;
			}
        }
    }
    while(transmission->c_apdu_len > transmission->out_offset);


    if(is_chain_block(&block_receive))
    {
        ret = CHAIN_IN;
    }

    return ret;
}

//static void init_null_block(block_t *block, icc_t *icc)
//{
//    uint8_t PCB = I_BLOCK_FLAG;
//
//    set_I_block_SN(&PCB, icc->SN_out);
//	set_I_block_chain(&PCB, 0);
//    init_block_send(block, PCB, (uint8_t *)0, 0);
//}

static void init_ack_block(block_t *block_send, icc_t *icc)
{
	init_R_block(block_send, icc->SN_in);
}

static void init_nak_block(block_t *nak, uint8_t sn, uint32_t err)
{
	init_R_block(nak, sn);
	if(err == ERR_PARITY || err == ERR_DEC)
	{
		nak->PCB |= 1; //DEC and/or parity error
	}
	else
	{
		nak->PCB |= 2;//Other error
	}
	nak->DEC = get_block_LRC(nak);
}

static uint32_t information_transfer_in(slot_t *slot, transmission_t *transmission)
{
    uint32_t ret = NO_ERR;
    icc_t *icc = slot->icc;
    block_t block_send, block_receive;

    while(transmission->in_offset < 1024)
    {
        init_ack_block(&block_send, icc);
        init_block_receive(&block_receive, transmission->r_apdu + transmission->in_offset, IFSD);
        ret = information_transfer(slot, &block_send, &block_receive);

        if(ret != NO_ERR)
        {
            return ret;
        }

        transmission->in_offset += block_receive.LEN;
        icc->SN_in++;

        if(!is_chain_block(&block_receive))
        {
        	return ret;
        }
    }

    return ERR_OVER_BUFFER_SIZE;
}

static uint32_t information_exchange(slot_t *slot, transmission_t *transmission)
{
    uint32_t ret;

    do
    {
        ret = information_transfer_out(slot, transmission);
        if(ret != ERR_OVER_RETRANSMIT)
        {
            break;
        }

        ret = BlockProtocol_ResynchronizationRequest(slot);

        if(ret != NO_ERR)
        {
            return ret;
        }
    }
    while(1);

    if(ret == CHAIN_IN)
    {
        do
        {
            ret = information_transfer_in(slot, transmission);
            if(ret != ERR_OVER_RETRANSMIT)
            {
                break;
            }

            ret = BlockProtocol_ResynchronizationRequest(slot);

            if(ret != NO_ERR)
            {
                return ret;
            }
        }
        while(1);

    }

    return ret;
}

static void Transmission_Init(transmission_t *transmission, uint8_t c_apdu[], uint32_t c_apdu_len, uint8_t r_apdu[])
{
    transmission->c_apdu = c_apdu;
    transmission->c_apdu_len = c_apdu_len;
    transmission->r_apdu = r_apdu;
    transmission->out_offset = 0;
    transmission->in_offset = 0;
    transmission->error_countor = 0;
}

uint32_t BlockProtocol_ExchangeApdu(slot_t *slot, uint8_t c_apdu[], uint32_t c_apdu_len,
                                    uint8_t r_apdu[], uint32_t *r_apdu_len)
{
    uint32_t ret = NO_ERR;
    //uint32_t r_len;
    transmission_t transmission;

    Transmission_Init(&transmission, c_apdu, c_apdu_len, r_apdu);
    ret = information_exchange(slot, &transmission);

    if(ret == NO_ERR)
    {
        *r_apdu_len = transmission.in_offset;
    }

    return ret;
}



